package parser.domain;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;

public class Factory {

	/**
	 * Creates a Grammar with the data in the file specified by filePath.The
	 * file must have the following format: 1st line - non terminals separated
	 * by space, 2nd row - terminals separated by space, 3rd row -starting
	 * symbol(must be in the non-terminal set), on each row starting with the
	 * 4th - a production rule of the form : A -> a B | eps
	 * 
	 * @param filePath
	 *            path to the file
	 * @return a Grammar
	 * @throws FileNotFoundException
	 *             if the path is invalid
	 */
	public static Grammar loadGrammarFromFile(String filePath)
			throws FileNotFoundException {
		File file = new File(filePath);
		checkFile(file);
		Scanner sc = new Scanner(file);
		Scanner lineSc;

		Set<NonTerminalSymbol> nonTerminals = new HashSet<NonTerminalSymbol>();
		Set<TerminalSymbol> terminals = new HashSet<TerminalSymbol>();
		Set<Production> productionRules = new HashSet<Production>();
		NonTerminalSymbol startingSymbol;

		// read the non terminals from the 1st row, separated by space
		lineSc = new Scanner(sc.nextLine());
		while (lineSc.hasNext()) {
			nonTerminals.add(new NonTerminalSymbol(lineSc.next()));
		}
		lineSc.close();

		// read the terminals from the 2nd row, separated by space
		lineSc = new Scanner(sc.nextLine());
		while (lineSc.hasNext()) {
			terminals.add(new TerminalSymbol(lineSc.next()));
		}
		lineSc.close();

		// read the starting symbol
		startingSymbol = new NonTerminalSymbol(sc.nextLine().trim());

		// read the productions
		while (sc.hasNext()) {
			lineSc = new Scanner(sc.nextLine());
			lineSc.useDelimiter(" *\\-\\> *| *\\| *");
			NonTerminalSymbol lhs = new NonTerminalSymbol(lineSc.next());

			// create a right hand part of a production
			List<Symbol> rhs = new ArrayList<Symbol>();
			Scanner rhsSc = new Scanner(lineSc.next());
			while (rhsSc.hasNext()) {
				String value = rhsSc.next();
				if (value.toLowerCase().equals(value))
					rhs.add(new TerminalSymbol(value));
				else
					rhs.add(new NonTerminalSymbol(value));
			}
			rhsSc.close();

			productionRules.add(new Production(lhs, rhs));
			lineSc.close();
		}
		sc.close();

		Grammar result = new Grammar(nonTerminals, terminals, productionRules,
				startingSymbol);
		return result;
	}

	private static void checkFile(File file) throws FileNotFoundException {
		if (!file.isFile() || !file.canRead()) {
			throw new FileNotFoundException("File not found or cannot be read.");
		}
	}
}
